// ============================== Configurations Start ==============================
/**
 * PLC 的相关配置，生产环境中，请根据现场的实际情况进行配置。
 * 
 * 【注意】
 *  - 配置 PLC 的参数之前，请先用 ModbusTcp 调试工具，例如 Modbus Poll，对 PLC 进行调试，以确认 PLC 的相关参数是否正常！！！
 * 
 */
const plcConfig = {
    host: "127.0.0.1",              // PLC 的 IP 地址
    port: 5020,                     // PLC 监听的端口号
    slaveId: 1,                     // PLC 中设置的 slaveId。
    labelerReachedAddr: {             // “AGV 已到达贴标机信号”的信号的地址信息
        type: "4x",                 // 地址类型
        number: 0,                  // 地址编号
        mark: "AGV已到达贴标机信号"      // 地址说明
    },
    labelingReleaseAddr: {           // “完成贴标并放行信号”的信号的地址信息
        type: "3x",
        number: 0,
        mark: "完成贴标并放行信号"
    },
    PrepositionAddr: {            // “AGV 到前置点信号”的信号的地址信息
        type: "4x",
        number: 1,
        mark: "AGV到前置点信号"
    },
    deliveryPackingAddr: {            // “AGV 可以放货打包信号”的信号的地址信息
        type: "3x",
        number: 1,
        mark: "AGV可以放货打包信号"
    },
    xStrappingAddr: {            // “启动 X 轴捆扎信号”的信号的地址信息
        type: "4x",
        number: 2,
        mark: "启动X轴捆扎信号"
    },
    xPackingCompletedAddr: {            // “X 轴捆扎已完成并请求 Y 轴捆扎信号”的信号的地址信息
        type: "3x",
        number: 2,
        mark: "X轴捆扎已完成并请求Y轴捆扎信号"
    },
    yStrappingAddr: {            // “启动 Y 轴捆扎信号”的信号的地址信息
        type: "4x",
        number: 3,
        mark: "启动Y轴捆扎信号"
    },
    yPackingCompletedAddr: {            // “Y 轴捆扎已完成信号”的信号的地址信息
        type: "3x",
        number: 3,
        mark: "Y轴捆扎已完成信号"
    },
    leaveAddr: {            // “AGV 已离开信号”的信号的地址信息
        type: "4x",
        number: 4,
        mark: "AGV已离开信号"
    },
    noPackingAddr: {            // “AGV 已到达打包机前置点-无需打包信号”的信号的地址信息
        type: "4x",
        number: 5,
        mark: "AGV 已到达打包机前置点-无需打包信号"
    },
    acrossAddr: {            // “AGV 可穿过打包位信号”的信号的地址信息
        type: "3x",
        number: 4,
        mark: "AGV 可穿过打包位信号"
    },
    leavedAddr: {            // “AGV 已离开打包机信号”的信号的地址信息
        type: "4x",
        number: 6,
        mark: "AGV 已离开打包机信号"
    },
    CX1Addr: {            // “产线1的发起任务信号”的信号的地址信息
        type: "4x",
        number: 7,
        mark: "产线1的发起任务信号"
    },
    CX2Addr: {            // “产线2的发起任务信号”的信号的地址信息
        type: "4x",
        number: 8,
        mark: "产线2的发起任务信号"
    }
}

// 系统正在使用的天风任务的名称，请勿随意修改！
const windTaskTransfer1 = "transfer1"
const windTaskTransfer2 = "transfer2"

// ------------------------------  Configurations End  ------------------------------

// ==========================================================================================================
// ============================== 以下内容为脚本代码逻辑，未经允许，请勿修改！！！ ==============================
// ==========================================================================================================
function boot() {
    initEvent()
    initHttpHandler()
}

// ============================== Events Start ==============================
function initEvent() {
    jj.registerTaskEventFunction("onTaskFinished")
    jj.defineScheduledFunctions(true, 5000, 2000, "executeTask", [])
}

function onTaskFinished(param: string) {
    logInfo(param)

    const paramObject = JSON.parse(param)
    const taskRecord = paramObject["taskRecord"]
    const taskRecordId = taskRecord["id"]

    const taskLabel = taskRecord["defLabel"]
    logInfo("taskLabel: " + taskLabel)
    if (taskLabel !== windTaskTransfer1 || taskLabel !== windTaskTransfer2) {
        logInfo("not customed task")
        return
    }

    logInfo("customed task")

    // 任务异常结束时，解锁起点和终点库位
    const status = taskRecord["status"]         // 1000：运行中，1001：终止，1002：暂停，1003：结束，1004：异常结束
    logInfo("taskStatus: " + status)
    if (status === 1001 || status === 1004) {
        if (taskLabel !== windTaskTransfer1) {
            jj.writeSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.labelerReachedAddr.type, plcConfig.labelerReachedAddr.number,0)
            jj.writeSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.PrepositionAddr.type, plcConfig.PrepositionAddr.number,0)
            jj.writeSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.xStrappingAddr.type, plcConfig.xStrappingAddr.number,0)
            jj.writeSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.yStrappingAddr.type, plcConfig.yStrappingAddr.number,0)
            jj.writeSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.leaveAddr.type, plcConfig.leaveAddr.number,0)
        } else if(taskLabel !== windTaskTransfer2){
            jj.writeSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.CX1Addr.type, plcConfig.CX1Addr.number,0)
        }
        const inputParams: any[] = JSON.parse(taskRecord["inputParams"])
        // 0: 起点和终点都还未被解锁；1: 解锁了起点
        const unlockStatus = getInputParamsValue(inputParams, "unlockStatus")
        const bySql = false
        logInfo("unlockStatus: " + unlockStatus)
        if (unlockStatus === 0)
            unlockSiteIfTaskFinishedAbnormally(taskRecordId, true, inputParams, bySql)
    }
}

function unlockSiteIfTaskFinishedAbnormally(taskRecordId: string, fromSite: boolean, inputParams: any, bySql: boolean) {
    const siteId = getInputParamsValue(inputParams, fromSite ? "fromSiteId" : "toSiteId")
    const siteMark = fromSite ? "起点" : "终点"
    const optDescription = `任务 ${taskRecordId} 异常结束时，解锁${siteMark}库位【${siteId}】`
    let message = `${optDescription}成功`
    if (typeof (siteId) === "string" && siteId) {
        if (bySql) {
            const sql = `UPDATE t_worksite SET locked=0, locked_by='' WHERE site_id='${siteId}' AND locked=1 AND locked_by='${taskRecordId}'`
            if (!jj.jdbcExecuteSql(sql)) message = `${optDescription}失败`
        } else {
            const result = unlockSite(siteId, siteMark, taskRecordId, true)
            if (result !== null) message = `${optDescription}失败`
        }
    } else {
        message = `${optDescription}失败，非法的库位ID。`
    }
    logInfo(message)
}

function executeTask() {
    var CX1 = jj.readSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.CX1Addr.type, plcConfig.CX1Addr.number)
    var CX2 = jj.readSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.CX2Addr.type, plcConfig.CX2Addr.number)
    var productLine
    var type
    if (CX1 != 0 && CX1 != null) {
        productLine = "CX1"
        type = "一号产线满包材"
    } else if (CX2 != 0 && CX2 != null) {
        productLine = "CX2"
        type = "二号产线满包材"
    } else {
        return
    }
    // 创建任务ID
    const taskRecordId = jj.createUuid();
    let lockState = 0
    try {
        const conditions = { content: [type], "locked": false }
        const siteResult = jj.findAvailableSitesByCondition(JSON.stringify(conditions), "ASC")
        if (siteResult === "null") {
            logInfo("不存在该产线的货物")
            return
        }
        const fromSiteList = JSON.parse(siteResult)
        var fromSiteId = fromSiteList[0].siteId
        const fromErrMsg = checkSiteAvailable(fromSiteId, "起点")
        lockState = 0
        if (fromErrMsg !== null) return failedResponse(fromErrMsg)
        const lockFromErrMsg = lockSite(fromSiteId, "起点", taskRecordId)
        if (lockFromErrMsg !== null) return failedResponse(lockFromErrMsg)
        const packingMaterial = productLine + "-BC"
        const finishedProduct = productLine + "-CP"
        const unlockStatus = 0
        //是否已经报警
        var noticeOperatorStatus = 0
        const inputParams: any = {
            packingMaterial, finishedProduct, fromSiteId, unlockStatus, noticeOperatorStatus
        };
        inputParams.host = plcConfig.host
        inputParams.port = plcConfig.port
        inputParams.slaveId = plcConfig.slaveId
        // AGV已到达贴标机的地址的信息
        inputParams.reachedAddrType = plcConfig.labelerReachedAddr.type
        inputParams.reachedAddrNo = plcConfig.labelerReachedAddr.number
        inputParams.reachedAddrMark = plcConfig.labelerReachedAddr.mark
        // 贴标机已完成贴标的地址的信息
        inputParams.releaseAddrType = plcConfig.labelingReleaseAddr.type
        inputParams.releaseAddrNo = plcConfig.labelingReleaseAddr.number
        inputParams.releaseAddrMark = plcConfig.labelingReleaseAddr.mark
        // AGV到前置点的的地址的信息
        inputParams.prepositionType = plcConfig.PrepositionAddr.type
        inputParams.prepositionNo = plcConfig.PrepositionAddr.number
        inputParams.prepositionMark = plcConfig.PrepositionAddr.mark
        // AGV可以放货打包的地址的信息
        inputParams.packingAddrType = plcConfig.deliveryPackingAddr.type
        inputParams.packingAddrNo = plcConfig.deliveryPackingAddr.number
        inputParams.packingAddrMark = plcConfig.deliveryPackingAddr.mark
        // 启动 X 轴捆扎的地址的信息
        inputParams.xStrappingAddrType = plcConfig.xStrappingAddr.type
        inputParams.xStrappingAddrNo = plcConfig.xStrappingAddr.number
        inputParams.xStrappingAddrMark = plcConfig.xStrappingAddr.mark
        // X 轴捆扎已完成并请求 Y 轴捆扎的地址的信息
        inputParams.xCompletedAddrType = plcConfig.xPackingCompletedAddr.type
        inputParams.xCompletedAddrNo = plcConfig.xPackingCompletedAddr.number
        inputParams.xCompletedAddrMark = plcConfig.xPackingCompletedAddr.mark
        // 启动 Y 轴捆扎的地址的信息
        inputParams.yStrappingAddrType = plcConfig.yStrappingAddr.type
        inputParams.yStrappingAddrNo = plcConfig.yStrappingAddr.number
        inputParams.yStrappingAddrMark = plcConfig.yStrappingAddr.mark
        // Y 轴捆扎已完成的地址的信息
        inputParams.yCompletedAddrType = plcConfig.yPackingCompletedAddr.type
        inputParams.yCompletedAddrNo = plcConfig.yPackingCompletedAddr.number
        inputParams.yCompletedAddrMark = plcConfig.yPackingCompletedAddr.mark
        // AGV 已离开的地址的信息
        inputParams.leaveAddrType = plcConfig.leaveAddr.type
        inputParams.leaveAddrNo = plcConfig.leaveAddr.number
        inputParams.leaveAddrMark = plcConfig.leaveAddr.mark
        const taskParams = {
            taskRecordId,
            taskLabel: windTaskTransfer1,
            inputParams: JSON.stringify(inputParams)
        };
        jj.createWindTask(JSON.stringify(taskParams))
        if (type == "一号产线满包材") {
            jj.writeSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.CX1Addr.type, plcConfig.CX1Addr.number, 0)
        } else {
            jj.writeSingleModbusValue(plcConfig.host, plcConfig.port, plcConfig.slaveId, plcConfig.CX2Addr.type, plcConfig.CX2Addr.number, 0)
        }
    } catch (e) {
        switch (lockState) {
            case 1:
                // 解锁起点
                unlockSite(fromSiteId, "起点", taskRecordId, true)
                break
            default:    // 0
                // 不用解锁库位
                break
        }
        logError(`create task failed, error=`, e)
        return failedResponse("未知异常导致创建任务失败，详见日志文件...")
    }
}


function selectPackingMaterialSite(param: string) {
    var paramJson = JSON.parse(param)
    const packingMaterial = paramJson["eventData"]
    const taskRecordId = paramJson.taskRecord.id
    const toSiteId = selectPackingMaterialSiteId(packingMaterial, taskRecordId)
    return {
        result: JSON.stringify({
            packingMaterialSite: toSiteId
        })
    }
}

function selectPackingMaterialSiteId(packingMaterial: string, taskRecordId: string) {
    var toSiteId = ""
    var toSite: Array<any> = [];
    toSite.push(packingMaterial);
    const packingMaterialStr = jj.getGroupSiteByStatus(toSite, 0, 0)
    const packingMaterialSiteList = JSON.parse(packingMaterialStr)
    if (packingMaterialSiteList == null) {
    } else if (packingMaterialSiteList.length == 2) {
        toSiteId = packingMaterial + "1"
    } else {
        if (packingMaterialSiteList[0].siteId == (packingMaterial + "1")) {
        }
        else {
            toSiteId = packingMaterialSiteList[0].siteId;
        }
    }
    return toSiteId
}

function selectFinishedProductSite(param: string) {
    var paramJson = JSON.parse(param)
    const finishedProduct = paramJson["eventData"]
    const taskRecordId = paramJson.taskRecord.id
    const toSiteId = selectFinishedProductSiteSiteId(finishedProduct, taskRecordId)
    return {
        result: JSON.stringify({
            finishedProductSite: toSiteId
        })
    }
}

function selectFinishedProductSiteSiteId(finishedProduct: string, taskRecordId: string) {
    var toSiteId = ""
    var toSite: Array<any> = [];
    toSite.push(finishedProduct);
    const finishedProductStr = jj.getGroupSiteByStatus(toSite, 1, 0)
    const finishedProductSiteList = JSON.parse(finishedProductStr)
    if (finishedProductSiteList == null) {
    } else if (finishedProductSiteList.length == 2) {
        toSiteId = finishedProduct + "1"
    } else {
        toSiteId = finishedProductSiteList[0].siteId
    }
    return toSiteId
}

function selectToSite(param: string) {
    var paramJson = JSON.parse(param)
    const taskRecordId = paramJson.taskRecord.id
    var noticeOperatorStatus = 0
    const toSiteId = selectToSiteId(taskRecordId, noticeOperatorStatus)
    return {
        result: JSON.stringify({
            toSiteId: toSiteId
        })
    }
}

function selectToSiteId(taskRecordId: string, noticeOperatorStatus: number) {
    var toSiteId = ""
    var toSite: Array<any> = []
    toSite.push("CK")
    const toSiteIdStr = jj.getGroupSiteByStatus(toSite, 0, 0)
    const toSiteIdList = JSON.parse(toSiteIdStr)
    if (toSiteIdList == null) {
        if (noticeOperatorStatus == 0)
            jj.noticeOperator("", "", `仓库区域里面库位全占用，无库位可放`, true, 2000);
    } else {
        toSiteId = toSiteIdList[0].siteId
    }
    var taskStatus = 1000
    while (taskStatus == 1000 && toSiteId == "") {
        jj.sleepThread(2000)
        taskStatus = getStatus(taskRecordId)
        toSiteId = selectToSiteId(taskRecordId, noticeOperatorStatus)
    }
    return toSiteId
}
// ------------------------------  Events End  ------------------------------


// ============================== Handles Start ==============================
function initHttpHandler() {
    jj.registerHandler("POST", "/script-api/create-task", "handleCreateTask", false)
}

function handleCreateTask(bodyStr: string) {
    // 创建任务ID
    const taskRecordId = jj.createUuid();
    let lockState = 0
    try {
        const body = JSON.parse(bodyStr);
        const robotTaskDef = body["robotTaskDef"]
        if (robotTaskDef == windTaskTransfer1) {
            const params = body["params"]
            var fromSiteId = params[0].value
            const productLine = params[1].value
            if (productLine == "CX1") {
                jj.setFilledWithContentBySiteId(fromSiteId, "一号产线满包材")
            } else {
                jj.setFilledWithContentBySiteId(fromSiteId, "二号产线满包材")
            }
        } else {
            logInfo(`create task: ${bodyStr}`)
            const params = body["params"]
            const fromSiteId = params[0].value
            const fromErrMsg = checkSiteAvailable(fromSiteId, "起点")
            lockState = 0
            if (fromErrMsg !== null) return failedResponse(fromErrMsg)
            const lockFromErrMsg = lockSite(fromSiteId, "起点", taskRecordId)
            if (lockFromErrMsg !== null) return failedResponse(lockFromErrMsg)
            const unlockStatus = 0
            const inputParams: any = {
                fromSiteId, unlockStatus
            };
            inputParams.host = plcConfig.host
            inputParams.port = plcConfig.port
            inputParams.slaveId = plcConfig.slaveId
            // AGV 已到达打包机前置点-无需打包的地址的信息
            inputParams.noPackingAddrType = plcConfig.noPackingAddr.type
            inputParams.noPackingAddrNo = plcConfig.noPackingAddr.number
            inputParams.noPackingAddrMark = plcConfig.noPackingAddr.mark
            // AGV 可穿过打包位的地址的信息
            inputParams.acrossAddrType = plcConfig.acrossAddr.type
            inputParams.acrossAddrNo = plcConfig.acrossAddr.number
            inputParams.acrossAddrMark = plcConfig.acrossAddr.mark
            // AGV 已离开打包机的地址的信息
            inputParams.leavedAddrType = plcConfig.leavedAddr.type
            inputParams.leavedAddrNo = plcConfig.leavedAddr.number
            inputParams.leavedAddrMark = plcConfig.leavedAddr.mark
            const taskParams = {
                taskRecordId,
                taskLabel: windTaskTransfer2,
                inputParams: JSON.stringify(inputParams)
            };
            jj.createWindTask(JSON.stringify(taskParams))
        }
        logInfo(`create task success, body=${bodyStr}`)
        return successResponse()
    }
    catch (e) {
        switch (lockState) {
            case 1:
                // 解锁起点
                unlockSite(fromSiteId, "起点", taskRecordId, true)
                break
            default:    // 0
                // 不用解锁库位
                break
        }
        logError(`create task failed, body=${bodyStr}, error=`, e)
        return failedResponse("未知异常导致创建任务失败，详见日志文件...")
    }
}

function successResponse(message: string = "OK"): OperatorResponse {
    return { code: 200, body: JSON.stringify({ code: 200, msg: message }) }
}

function failedResponse(message: string): OperatorResponse {
    logError(message, "")
    return { code: 200, body: JSON.stringify({ code: 300, msg: message }) }
}

/*
{
    // HTTP 响应码，只有当值为 200 时，手持端才能解析 body 字段的数据
    "code": 200,

    // boyd.code: 请求的响应码，200 表示请求成功，300 表示请求失败，请求失败时，手持端会显示 msg 的内容。
    // body.message: 响应的内容。
    "body": "{\"code\": ${code},\"msg\": ${msg}}"
}
*/
interface OperatorResponse {
    code: number        // HTTP 响应码，为固定值 200，否者手持端无法解析响应正文。
    body: string        // JSON.stringify(ResponseBody object)
}

interface ResponseBody {
    code: number        // 200: 请求成功; 300: 请求失败，手持端会显示 msg 的内容。
    msg: string         // 默认值为 "OK"
}
// ------------------------------  Handles End  ------------------------------

// ============================== Utils Start ==============================
/**
 * 判断目标库位是否是未被锁定的有效库位
 * @param siteId 目标库位的名称
 * @param siteMark 目标库位的描述
 * @returns 如果是未被锁定的有效库位，则返回null；否则返回异常信息
 */
function checkSiteAvailable(siteId: string, siteMark: SiteMark): string | null {
    const conditions = { siteIds: [siteId] }
    const siteResult = jj.findAvailableSitesByCondition(JSON.stringify(conditions), "ASC")
    if (siteResult === "null")
        return `不存在${siteMark}库位【${siteId}】`
    const fromSiteList = JSON.parse(siteResult)
    const count = fromSiteList.length
    if (count === 0) {
        return `不存在${siteMark}库位【${siteId}】`
    } else if (count > 1) {
        return `系统中存在重复的${siteMark}库位【${siteId}】`
    } else {
        const site = fromSiteList[0]
        if (site.locked) {
            return `${siteMark}库位【${siteId}】已经被其他任务【${site.lockedBy || "-"}】锁定`
        } else {
            return null
        }
    }
}

/**
 * 判断是否存在未结束的从 fromSiteId 到 toSiteId 的任务，防止重复下单。
 * @param fromSiteId 任务的起点库位名称
 * @param toSiteId 任务的终点库位名称
 * @returns 不存在则返回null，否则返回异常信息。
 */
function checkTaskRecordRepected(fromSiteId: string, toSiteId: string): string | null {
    const outOrderNo = `${fromSiteId}-${toSiteId}`
    // 查询所有从 fromSiteId 到 toSiteId ，且还未结束的天风任务实例
    const sql = `SELECT count(1) FROM t_windtaskrecord WHERE out_order_no='${outOrderNo}' AND status IN (1000, 1002)`
    const matchedtaskRecordsCount = jj.jdbcQueryCount(sql)
    if (matchedtaskRecordsCount === null)
        return `查询任务失败：${sql}`
    else if (matchedtaskRecordsCount > 0)
        return `存在【${matchedtaskRecordsCount}】条从【${fromSiteId}】到【${toSiteId}】，且还未结束的任务，请勿重复下单！`
    else return null
}

/**
 * 锁定目标库位
 * @param siteId 目标库位的名称
 * @param siteMark 目标库位的描述
 * @returns 成功时，返回null；失败时，返回异常信息，或者抛异常
 */
function lockSite(siteId: string, siteMark: SiteMark, lockedBy: string): string | null {
    // 此方法未在 catch 中被调用，需要抛异常，sliently = false
    return changeSiteLockedState(siteId, siteMark, true, lockedBy, false)
}

/**
 * 解锁目标库位
 * @param siteId 目标库位的名称
 * @param siteMark 目标库位的描述
 * @param sliently true:此方法不抛异常，但是会返回异常信息；false:此方法会抛异常
 * @returns 成功时，返回null；失败时，返回异常信息，或者抛异常
 */
function unlockSite(siteId: string, siteMark: SiteMark, lockedBy: string, sliently: boolean): string | null {
    // 此方法在 catch 中被调用时，不能抛异常，sliently = true
    // 此方法在 try 中被调用时，可以抛异常，sliently = false
    return changeSiteLockedState(siteId, siteMark, false, lockedBy, sliently)
}

/**
 * 修改库位的锁定状态
 * @param siteId 目标库位的名称
 * @param siteMark 目标库位的描述
 * @param lockIt true:执行锁定库位的操作；false:执行解锁库位的操作
 * @param sliently true:此方法不抛异常，但是会返回异常信息；false:此方法会抛异常
 * @returns 成功时，返回null；失败时，返回异常信息，或者抛异常
 */
function changeSiteLockedState(siteId: string, siteMark: SiteMark, lockIt: boolean, lockedBy: string, sliently: boolean): string | null {
    const optMark = lockIt ? `锁定${siteMark}` : `解锁${siteMark}`
    const optDescription = `${optMark} 库位【${siteId}】`
    try {
        const lockedByCondition = lockIt ? "" : lockedBy
        const lockedByNewValue = lockIt ? lockedBy : ""
        const conditions = { siteIds: [siteId], lockedBy: lockedByCondition }
        const newValues = { locked: lockIt, lockedBy: lockedByNewValue }
        const count = jj.updateSitesByCondition(JSON.stringify(conditions), JSON.stringify(newValues))
        if (count <= 0) {
            if (!lockIt) return `${optDescription}失败`
            else {
                return lockSiteIfLockedByIsNull(conditions, newValues, optDescription)
            }
        } else if (count > 1) {
            return `${optDescription}失败，更新了【${count}】条记录`
        } else {
            logInfo(`${optDescription}成功`)
            return null
        }
    } catch (e) {
        logError(optDescription, e)
        if (sliently) return `${optDescription} 失败，详见日志文件...`
        else throw e
    }
}

/**
 * 当库位的 lockedBy 字段为 null 时，尝试锁定库位
 * @param conditions 锁定库位的条件，函数中会强制指定 conditions.lockedBy=null
 * @param newValues 库位被锁定之后的状态。
 * @param remark 操作的描述
 * @returns 操作成功时，返回null；操作失败时，返回异常信息
 */
function lockSiteIfLockedByIsNull(conditions: any, newValues: any, remark: string): string | null {
    conditions.lockedBy = null
    const conditionsStr = JSON.stringify(conditions)
    const newValuesStr = JSON.stringify(newValues)
    logInfo(`try to lock site when it's locked by null, conditions=${conditionsStr}, newValue=${newValuesStr}`)
    const count = jj.updateSitesByCondition(conditionsStr, newValuesStr)
    if (count <= 0) {
        return `${remark}失败`
    } else if (count > 1) {
        return `${remark}，更新了【${count}】条记录`
    } else {
        logInfo(`${remark}成功`)
        return null
    }
}

function getInputParamsValue(inputParams: any[], failedName: string): any {
    return inputParams.find(i => i.name === failedName).defaultValue
}

/**
 * 记录消息到后台的日志文件中
 * @param message 消息内容
 */
function logInfo(message: string) {
    jj.getLogger().info(message)
}

/**
 * 记录异常信息到后台的日志文件中
 * @param message 消息内容
 * @param e 异常
 */
function logError(message: string, e: any) {
    jj.getLogger().error(message, e)
}

//查询天风任务状态
function getStatus(taskRecordId: string) {
    var sql = "SELECT `status` FROM t_windtaskrecord WHERE id = " + "'" + taskRecordId + "'"
    return JSON.parse(jj.jdbcQuery(sql))[0].status
}

type SiteMark = "起点" | "终点"
// ------------------------------  Utils End  ------------------------------
